Piano notes book, powered by Astro and React.
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 

137 wiersze
3.5 KiB

  1. import createVerovioModule from 'verovio/wasm';
  2. import { VerovioToolkit } from 'verovio/esm';
  3. import { readFile, readdir } from 'node:fs/promises';
  4. import { JSDOM } from 'jsdom';
  5. import type {APIRoute, GetStaticPaths} from 'astro';
  6. // @ts-ignore
  7. import tailwindConfig from '../../../tailwind.config.mjs';
  8. const filter = (musicXml: string) => {
  9. const jsdom = new JSDOM(musicXml, { pretendToBeVisual: true, contentType: 'image/svg+xml' });
  10. const win = jsdom.window;
  11. // const win = jsdom.window;
  12. return win.document.documentElement.outerHTML;
  13. };
  14. const processOutput = (xmlData: string) => {
  15. const jsdom = new JSDOM(xmlData, { pretendToBeVisual: true, contentType: 'image/svg+xml' });
  16. const win = jsdom.window;
  17. const [svgElemsRoot, svgElemsMain] = Array.from(win.document.getElementsByTagName('svg'));
  18. if (typeof svgElemsRoot === 'undefined') {
  19. return '';
  20. }
  21. if (typeof svgElemsMain === 'undefined') {
  22. return '';
  23. }
  24. // svgElemsRoot.getAttributeNames().forEach((a) => {
  25. // const attr = svgElemsRoot.getAttribute(a);
  26. // if (!attr) {
  27. // return;
  28. // }
  29. // svgElemsMain.setAttribute(a, attr);
  30. // });
  31. Array.from(svgElemsRoot.children).forEach((h) => {
  32. if (h !== svgElemsMain) {
  33. h.remove();
  34. if (h.tagName.toLowerCase() === 'desc') {
  35. return;
  36. }
  37. if (h.tagName.toLowerCase() === 'style') {
  38. h.innerHTML = h.innerHTML.replace(/Times,serif/g, tailwindConfig.theme.fontFamily.body.join(','));
  39. }
  40. if (svgElemsMain.children[0]) {
  41. svgElemsMain.insertBefore(h, svgElemsMain.children[0]);
  42. return;
  43. }
  44. svgElemsMain.appendChild(h);
  45. }
  46. });
  47. Array.from(win.document.getElementsByClassName('pgHead')).forEach(h => {
  48. h.remove();
  49. });
  50. Array.from(win.document.getElementsByClassName('pgFoot')).forEach(h => {
  51. h.remove();
  52. });
  53. Array.from(win.document.getElementsByTagName('defs')).forEach(h => {
  54. h.remove();
  55. if (svgElemsMain) {
  56. if (svgElemsMain.children[0]) {
  57. svgElemsMain.insertBefore(h, svgElemsMain.children[0]);
  58. } else {
  59. svgElemsMain.appendChild(h);
  60. }
  61. }
  62. });
  63. return `<?xml version="1.0" encoding="utf-8"?>${svgElemsMain.outerHTML}`
  64. };
  65. export const GET: APIRoute = async ({ params }) => {
  66. const verovioModule = await createVerovioModule();
  67. const score = await readFile(`public/scores/${params.asset}.musicxml`, 'utf-8');
  68. const verovioToolkit = new VerovioToolkit(verovioModule);
  69. const filteredScore = filter(score);
  70. const isSuccessful = verovioToolkit.loadData(filteredScore);
  71. if (!isSuccessful) {
  72. return new Response(null, { status: 500 });
  73. }
  74. verovioToolkit.setOptions({
  75. breaks: 'none',
  76. font: 'Bravura',
  77. bottomMarginHeader: 0,
  78. bottomMarginArtic: 0,
  79. bottomMarginHarm: 0,
  80. topMarginHarm: 0,
  81. topMarginArtic: 0,
  82. topMarginPgFooter: 0,
  83. pageMarginTop: 0,
  84. pageMarginBottom: 0,
  85. pageMarginRight: 0,
  86. pageMarginLeft: 0,
  87. defaultBottomMargin: 0,
  88. defaultTopMargin: 0,
  89. defaultLeftMargin: 0,
  90. defaultRightMargin: 0,
  91. });
  92. let data: string;
  93. try {
  94. const raw = verovioToolkit.renderToSVG(1)
  95. .replace(/xmlns:mei="(.+?)"/g, '')
  96. .replace(/xlink:/g, '');
  97. data = processOutput(raw);
  98. } catch (err) {
  99. console.error(err);
  100. return new Response(null, { status: 500 });
  101. }
  102. return new Response(
  103. data,
  104. {
  105. headers: {
  106. 'Content-Type': 'image/svg+xml',
  107. },
  108. status: 200,
  109. }
  110. );
  111. };
  112. export const getStaticPaths: GetStaticPaths = async () => {
  113. const files = await readdir('public/scores');
  114. return files
  115. .filter((f) => f.endsWith('.musicxml'))
  116. .map((f) => ({
  117. params: {
  118. asset: f.replace(/\.musicxml/g, ''),
  119. },
  120. }));
  121. };